package com.xinlong.shop.core.blind.service.impl;

import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.xinlong.shop.core.blind.core.BlindBoxDraw;
import com.xinlong.shop.core.blind.core.ProbEntity;
import com.xinlong.shop.core.blind.entity.*;
import com.xinlong.shop.core.blind.entity.dto.BlindBoxOrderDTO;
import com.xinlong.shop.core.blind.mapper.BlindBoxOrderMapper;
import com.xinlong.shop.core.blind.mapstruct.BlindBoxGoodsStruct;
import com.xinlong.shop.core.blind.service.IBlindBoxGoodsService;
import com.xinlong.shop.core.blind.service.IBlindBoxOrderItemService;
import com.xinlong.shop.core.blind.service.IBlindBoxOrderService;
import com.xinlong.shop.core.blind.service.IBlindBoxService;
import com.xinlong.shop.core.util.OrderSnTypeEnum;
import com.xinlong.shop.core.util.OrderUtil;
import com.xinlong.shop.framework.exception.ServiceException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

/**
 * <p>
 * 盲盒订单表 服务实现类
 * </p>
 *
 * @author Sylow
 * @since 2023-06-13
 */
@Service
public class BlindBoxOrderServiceImpl extends ServiceImpl<BlindBoxOrderMapper, BlindBoxOrder> implements IBlindBoxOrderService {

    private final static Logger logger = LoggerFactory.getLogger(BlindBoxOrderServiceImpl.class);

    private final OrderUtil orderUtil;
    private final BlindBoxDraw blindBoxDraw;
    private final IBlindBoxService blindBoxService;
    private final IBlindBoxGoodsService blindBoxGoodsService;
    private final BlindBoxGoodsStruct blindBoxGoodsStruct;
    private final IBlindBoxOrderItemService blindBoxOrderItemService;

    public BlindBoxOrderServiceImpl(OrderUtil orderUtil, BlindBoxDraw blindBoxDraw, IBlindBoxService blindBoxService, IBlindBoxGoodsService blindBoxGoodsService, BlindBoxGoodsStruct blindBoxGoodsStruct, IBlindBoxOrderItemService blindBoxOrderItemService) {
        this.orderUtil = orderUtil;
        this.blindBoxDraw = blindBoxDraw;
        this.blindBoxService = blindBoxService;
        this.blindBoxGoodsService = blindBoxGoodsService;
        this.blindBoxGoodsStruct = blindBoxGoodsStruct;
        this.blindBoxOrderItemService = blindBoxOrderItemService;
    }

    @Override
    public void update(BlindBoxOrder blindBoxOrder, Integer id) {
        UpdateWrapper<BlindBoxOrder> updateWrapper = new UpdateWrapper<>();
        updateWrapper.eq("id", id);
        this.update(blindBoxOrder, updateWrapper);
    }
    @Override
    public void delete(Integer id) {
        UpdateWrapper<BlindBoxOrder> deleteWrapper = new UpdateWrapper<>();
        deleteWrapper.eq("id", id);
        this.remove(deleteWrapper);
    }

    @Override
    public BlindBoxOrder createOrder(BlindBox blindBox, String ruleId, Integer memberId) {

        String orderSn = orderUtil.getOrderSn(OrderSnTypeEnum.BLIND_BOX.getCode());

        Map<String, Object> priceRule = null;

        // 找到价格规则
        for (Map<String, Object> rule : blindBox.getPriceRules()) {
            if (ruleId.equals(rule.get("id").toString())) {
                priceRule = rule;
                break;
            }
        }

        if (priceRule == null) {
            throw new ServiceException("价格规则不存在");
        }

        BigDecimal price = new BigDecimal(priceRule.get("price").toString());
        int buyNum = Double.valueOf(priceRule.get("buyNum").toString()).intValue();  // 盲盒开奖次数

        BlindBoxOrder blindBoxOrder = new BlindBoxOrder();
        blindBoxOrder.setOrderSn(orderSn);
        blindBoxOrder.setBlindBoxId(blindBox.getId());
        blindBoxOrder.setMemberId(memberId);
        blindBoxOrder.setBlindBoxNum(buyNum);
        blindBoxOrder.setPrice(price);
        blindBoxOrder.setPriceRule(priceRule);
        blindBoxOrder.setCreateTime(DateUtil.currentSeconds());

        boolean result = this.save(blindBoxOrder);

        if (result) {
            return blindBoxOrder;
        } else {
            throw new ServiceException("创建盲盒订单失败");
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean paySuccess(String orderSn, String payOrderSn) {
        /**
         * 备注： 目前库存判断是抽奖前判断的，如果抽奖得出商品数量大于库存数量，那么数据库库存会变为负数  后期优化
         */
        BlindBoxOrder blindBoxOrder = this.getOne(new QueryWrapper<BlindBoxOrder>().eq("order_sn", orderSn));
        if (blindBoxOrder != null) {
            blindBoxOrder.setPaymentOrderNo(payOrderSn);
            blindBoxOrder.setPayTime(DateUtil.currentSeconds());
            blindBoxOrder.setStatus(BlindBoxOrderStatusEnum.PAY.getCode());
            // 这里可以优化成 新增一个抽奖结束状态，然后抽奖做异步处理
            boolean updateResult =  this.updateById(blindBoxOrder);
            if (updateResult) {
                BlindBox blindBox = blindBoxService.getById(blindBoxOrder.getBlindBoxId());
                // 产出的盲盒商品列表  抽奖有多次
                List<BlindBoxGoods> outBlinxBoxList = new ArrayList<>();
                for(int i = 0; i < blindBoxOrder.getBlindBoxNum().intValue(); i++) {
                    // 抽奖
                    BlindBoxGoods outBlinBoxGoods = getGoods(blindBox);
                    // 添加进数组
                    outBlinxBoxList.add(outBlinBoxGoods);

                }

                List<BlindBoxOrderItem> orderItemList = new ArrayList<>();
                String ids = "";    // 盲盒商品ids  11,22,33 这种格式，用于判断是否重复
                // 保存盲盒商品
                for(BlindBoxGoods blindBoxGoods : outBlinxBoxList) {
                    // 如果有重复商品 数量加一就行
                    if (ids.indexOf("|" + blindBoxGoods.getGoodsId().toString() + "|") != -1) {
                        // 已有的商品列表里面找到对应商品
                        for (BlindBoxOrderItem orderItem : orderItemList) {
                            if (orderItem.getGoodsId().equals(blindBoxGoods.getGoodsId())) {
                                orderItem.setNum(orderItem.getNum() + 1);
                                break;
                            }
                        }
                    } else {
                        // 新增订单项
                        BlindBoxOrderItem orderItem = blindBoxGoodsStruct.toBlindBoxOrderItem(blindBoxGoods);
                        orderItem.setId(null);
                        orderItem.setBlindBoxOrderId(blindBoxOrder.getId());
                        orderItem.setMemberId(blindBoxOrder.getMemberId());
                        orderItem.setNum(1);
                        orderItem.setCreateTime(DateUtil.currentSeconds());
                        ids += "|" + orderItem.getGoodsId().toString() + "|";
                        orderItemList.add(orderItem);
                    }
                }
                boolean batchResult = blindBoxOrderItemService.saveBatch(orderItemList);
                if (batchResult) {
                    // 循环 更新盲盒商品库存
                    for (BlindBoxOrderItem orderItem : orderItemList) {
                        this.blindBoxGoodsService.reduceStock(orderItem.getBlindBoxId(), orderItem.getGoodsId(), orderItem.getNum());
                    }
                    return true;
                } else {
                    logger.error("保存盲盒订单商品失败");
                    throw new ServiceException("保存盲盒订单商品失败");
                }
            } else {
                logger.error("更新盲盒订单状态失败");
                throw new ServiceException("更新盲盒订单状态失败");
            }
        }
        return false;
    }

    @Override
    public IPage<BlindBoxOrderDTO> findByPage(IPage<BlindBoxOrderDTO> page, BlindBoxOrderQueryParam queryParam) {
        QueryWrapper queryWrapper = new QueryWrapper();
        queryWrapper.like(StrUtil.isNotBlank(queryParam.getOrderSn()), "order_sn", queryParam.getOrderSn());
        queryWrapper.like(StrUtil.isNotBlank(queryParam.getMemberName()), "m.member_name", queryParam.getMemberName());
        queryWrapper.like(StrUtil.isNotBlank(queryParam.getNickname()), "m.nickname", queryParam.getNickname());
        queryWrapper.like(StrUtil.isNotBlank(queryParam.getMemberMobile()), "m.mobile", queryParam.getMemberMobile());
        queryWrapper.like(StrUtil.isNotBlank(queryParam.getBlindBoxName()), "b.blind_box_name", queryParam.getBlindBoxName());
        queryWrapper.eq(queryParam.getStatus() != null, "status", queryParam.getStatus());
        return this.baseMapper.findByPage(page, queryWrapper);
    }

    @Override
    public List<BlindBoxOrderDTO> findByMemberId(Integer memberId) {
        QueryWrapper queryWrapper = new QueryWrapper();
        queryWrapper.eq(memberId != null, "member_id", memberId);

        return this.baseMapper.findByList(queryWrapper);
    }

    private BlindBoxGoods getGoods(BlindBox blindBox) {
        /**
         * 盲盒抽奖 先得到商品标签
         */
        String goodsTag = getGoodsTag(blindBox);
        // 得到对应商品列表
        List<BlindBoxGoods> blindBoxGoodsList = blindBoxGoodsService.findByBlindBoxIdAndTag(blindBox.getId(), goodsTag);
        // 检查一下商品库存
        boolean isHaveStock = false;
        for(BlindBoxGoods blindBoxGoods : blindBoxGoodsList) {
            if (blindBoxGoods.getStock() > 0) {
                isHaveStock = true;
                break;
            }
        }
        if (!isHaveStock) {
            //throw new ServiceException("商品库存不足");
            // 没有库存就递归开盒
            return getGoods(blindBox);
        }
        /**
         * 再进行抽奖 得到对应盲盒商品
         */
        BlindBoxGoods outBlinBoxGoods = getGoods(blindBoxGoodsList);
        return outBlinBoxGoods;
    }

    /**
     * 盲盒抽奖 先抽出对应的商品标签
     * @param blindBox
     * @return
     */
    private String getGoodsTag(BlindBox blindBox) {
        List<Map> list = blindBox.getProduceRules();
        List<ProbEntity> probList = new ArrayList<>();
        for(Map map : list) {
            ProbEntity prob = new ProbEntity();
            prob.setId(map.get("id").toString());
            prob.setRate(Double.valueOf(map.get("odds").toString()).intValue());
            probList.add(prob);
        }

        // 抽奖进行产出 产出的是商品标签
        ProbEntity outProbEntity = blindBoxDraw.draw(probList);
        return outProbEntity.getId().toString();
    }

    /**
     * 盲盒抽奖 得到盲盒商品信息
     * @param blindBoxGoodsList
     * @return
     */
    private BlindBoxGoods getGoods(List<BlindBoxGoods> blindBoxGoodsList) {
        List<ProbEntity> probList = new ArrayList<>();
        for(BlindBoxGoods blindBoxGoods : blindBoxGoodsList) {
            // 没有库存了 不参与抽奖
            if (blindBoxGoods.getStock() < 1) {
                continue;
            }
            ProbEntity prob = new ProbEntity();
            prob.setId(blindBoxGoods.getId());
            prob.setRate(blindBoxGoods.getOdds());
            probList.add(prob);
        }

        // 抽奖进行产出 产出的是商品标签
        ProbEntity outProbEntity = blindBoxDraw.draw(probList);
        for(BlindBoxGoods blindBoxGoods : blindBoxGoodsList) {
            if (blindBoxGoods.getId().equals(outProbEntity.getId())) {
                return blindBoxGoods;
            }
        }
        return null;
    }

}
